/**
 * 测试集群负载均衡
 * 向服务器发送多个并发请求，观察负载分布
 */

const http = require('http');

// 配置
const PORT = 3000;
const HOST = 'localhost';
const TOTAL_REQUESTS = 20; // 总请求数
const CONCURRENT_REQUESTS = 4; // 每批请求的数量
const REQUEST_DELAY = 100; // 每批请求之间的延迟（毫秒）

// 存储响应结果
const results = [];
let completedRequests = 0;

console.log(`开始向 http://${HOST}:${PORT} 发送 ${TOTAL_REQUESTS} 个请求...`);

// 发送单个请求
function sendRequest(id) {
  return new Promise((resolve) => {
    const startTime = Date.now();
    
    const req = http.request({
      hostname: HOST,
      port: PORT,
      path: `/test?id=${id}`,
      method: 'GET',
    }, (res) => {
      let data = '';
      
      res.on('data', (chunk) => {
        data += chunk;
      });
      
      res.on('end', () => {
        const endTime = Date.now();
        const duration = endTime - startTime;
        
        try {
          const responseData = JSON.parse(data);
          
          // 存储结果
          results.push({
            requestId: id,
            workerId: responseData.pid,
            duration: duration,
            processingTime: responseData.processingTime,
          });
          
          console.log(`请求 #${id} 被工作进程 ${responseData.pid} 处理，耗时 ${duration}ms`);
          resolve();
        } catch (e) {
          console.error(`解析响应数据时出错: ${e.message}`);
          resolve();
        }
      });
    });
    
    req.on('error', (error) => {
      console.error(`请求 #${id} 出错: ${error.message}`);
      resolve();
    });
    
    req.end();
  });
}

// 同时发送一批请求，并等待所有请求完成；再发送下一批请求，而不是一个一个往里补充请求
// 所以当 CONCURRENT_REQUESTS（并发控制） 为 5 时，监听 3000 端口的线程为 4 时，最后一个请求触发 Keep-Alive 复用连接机制，被同一个线程处理
async function sendBatch(startId, count) {
  const promises = [];
  
  for (let i = 0; i < count; i++) {
    const requestId = startId + i;
    if (requestId <= TOTAL_REQUESTS) {
      promises.push(sendRequest(requestId));
      completedRequests++;
    }
  }
  
  await Promise.all(promises);
}

// 按批次发送所有请求
async function runTest() {
  let currentId = 1;
  
  while (currentId <= TOTAL_REQUESTS) {
    const batchSize = Math.min(CONCURRENT_REQUESTS, TOTAL_REQUESTS - currentId + 1);
    await sendBatch(currentId, batchSize);
    currentId += batchSize;
    
    if (currentId <= TOTAL_REQUESTS) {
      // 在批次之间添加短暂延迟
      await new Promise(resolve => setTimeout(resolve, REQUEST_DELAY));
    }
  }
  
  // 分析结果
  analyzeResults();
}

// 分析请求分布
function analyzeResults() {
  console.log('\n--- 测试结果 ---');
  
  // 按工作进程ID分组
  const workerStats = {};
  
  for (const result of results) {
    const workerId = result.workerId;
    
    if (!workerStats[workerId]) {
      workerStats[workerId] = {
        count: 0,
        totalDuration: 0,
      };
    }
    
    workerStats[workerId].count++;
    workerStats[workerId].totalDuration += result.duration;
  }
  
  // 输出每个工作进程的负载统计
  console.log('\n工作进程负载分布:');
  
  for (const workerId in workerStats) {
    const stats = workerStats[workerId];
    const percentage = (stats.count / results.length * 100).toFixed(2);
    const avgDuration = (stats.totalDuration / stats.count).toFixed(2);
    
    console.log(`工作进程 ${workerId}: 处理了 ${stats.count} 个请求 (${percentage}%), 平均响应时间: ${avgDuration}ms`);
  }
  
  // 计算总体统计
  const totalDuration = results.reduce((sum, result) => sum + result.duration, 0);
  const avgDuration = (totalDuration / results.length).toFixed(2);
  const minDuration = Math.min(...results.map(r => r.duration));
  const maxDuration = Math.max(...results.map(r => r.duration));
  
  console.log('\n总体统计:');
  console.log(`总请求数: ${results.length}`);
  console.log(`平均响应时间: ${avgDuration}ms`);
  console.log(`最短响应时间: ${minDuration}ms`);
  console.log(`最长响应时间: ${maxDuration}ms`);
  console.log(`负载均衡分布标准偏差: ${calculateStdDev(Object.values(workerStats).map(s => s.count)).toFixed(2)}`);
}

// 计算标准偏差
function calculateStdDev(values) {
  const mean = values.reduce((sum, val) => sum + val, 0) / values.length;
  const squareDiffs = values.map(val => Math.pow(val - mean, 2));
  const avgSquareDiff = squareDiffs.reduce((sum, val) => sum + val, 0) / squareDiffs.length;
  return Math.sqrt(avgSquareDiff);
}

// 运行测试
runTest();
